import asyncio
import json
from typing import Optional, Dict, Any
from datetime import datetime, timedelta
import httpx
from fastapi import HTTPException, status
from config import get_settings
from utils.auth import generate_verification_code

# 获取配置
settings = get_settings()

redis_client = None

class SMSService:
    """
    短信服务类
    """
    
    def __init__(self):
        self.redis_client = None
        self._codes: Dict[str, Dict[str, Any]] = {}
        self._rate_limit: Dict[str, datetime] = {}
        # 默认使用阿里云；若配置缺失则回退为mock
        if (
            getattr(settings, 'aliyun_access_key_id', None)
            and getattr(settings, 'aliyun_access_key_secret', None)
            and getattr(settings, 'aliyun_sms_sign_name', None)
            and getattr(settings, 'aliyun_sms_template_code', None)
        ):
            self.sms_provider = "aliyun"
        else:
            self.sms_provider = "mock"  # mock/aliyun/tencent/twilio
    
    async def send_verification_code(self, phone: str, scene: str = "login") -> Dict[str, Any]:
        """
        发送短信验证码
        
        Args:
            phone: 手机号
            scene: 使用场景 (login/register/reset_password)
        
        Returns:
            Dict[str, Any]: 发送结果
        
        Raises:
            HTTPException: 发送失败时抛出异常
        """
        # 检查发送频率限制
        if await self._check_rate_limit(phone):
            raise HTTPException(
                status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                detail="发送过于频繁，请稍后再试"
            )
        
        # 生成4位验证码（登录场景）
        import random
        code = str(random.randint(1000, 9999))
        
        # 存储验证码到Redis（5分钟过期）
        await self._store_verification_code(phone, code, scene)
        
        # 发送短信
        success = await self._send_sms(phone, code, scene)
        
        if not success:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="短信发送失败"
            )
        
        # 记录发送时间（用于频率限制）
        await self._record_send_time(phone)
        
        return {
            "success": True,
            "message": "验证码发送成功",
            "expires_in": 300  # 5分钟
        }
    
    async def verify_code(self, phone: str, code: str, scene: str = "login") -> bool:
        """
        验证短信验证码
        
        Args:
            phone: 手机号
            code: 验证码
            scene: 使用场景
        
        Returns:
            bool: 验证是否成功
        """
        key = f"{scene}:{phone}"
        entry = self._codes.get(key)
        if not entry:
            return False
        if datetime.now() > entry.get("expire_at"):
            self._codes.pop(key, None)
            return False
        ok = str(entry.get("code")) == str(code)
        if ok:
            self._codes.pop(key, None)
        return ok
    
    async def _check_rate_limit(self, phone: str) -> bool:
        """
        检查发送频率限制
        
        Args:
            phone: 手机号
        
        Returns:
            bool: 是否超出频率限制
        """
        last_time = self._rate_limit.get(phone)
        if last_time and (datetime.now() - last_time) < timedelta(seconds=60):
            return True
        return False
    
    async def _store_verification_code(self, phone: str, code: str, scene: str):
        """
        存储验证码到Redis
        
        Args:
            phone: 手机号
            code: 验证码
            scene: 使用场景
        """
        key = f"{scene}:{phone}"
        self._codes[key] = {
            "code": code,
            "expire_at": datetime.now() + timedelta(seconds=300)
        }
    
    async def _record_send_time(self, phone: str):
        """
        记录发送时间
        
        Args:
            phone: 手机号
        """
        self._rate_limit[phone] = datetime.now()
    
    async def _send_sms(self, phone: str, code: str, scene: str) -> bool:
        """
        发送短信（具体实现）
        
        Args:
            phone: 手机号
            code: 验证码
            scene: 使用场景
        
        Returns:
            bool: 发送是否成功
        """
        if self.sms_provider == "mock":
            # 模拟发送（开发环境）
            print(f"[模拟短信] 发送到 {phone}: 验证码 {code}，场景: {scene}")
            return True
        
        elif self.sms_provider == "aliyun":
            return await self._send_aliyun_sms(phone, code, scene)
        
        elif self.sms_provider == "tencent":
            return await self._send_tencent_sms(phone, code, scene)
        
        else:
            return False
    
    async def _send_aliyun_sms(self, phone: str, code: str, scene: str) -> bool:
        """
        发送阿里云短信
        
        Args:
            phone: 手机号
            code: 验证码
            scene: 使用场景
        
        Returns:
            bool: 发送是否成功
        """
        try:
            from alibabacloud_dysmsapi20170525.client import Client as Dysmsapi20170525Client
            from alibabacloud_tea_openapi import models as open_api_models
            from alibabacloud_dysmsapi20170525 import models as dysms_models
            from alibabacloud_tea_util import models as util_models
            import json

            ak_id = getattr(settings, 'aliyun_access_key_id', '')
            ak_secret = getattr(settings, 'aliyun_access_key_secret', '')
            if not ak_id or not ak_secret:
                print("阿里云短信发送异常: 缺少AK配置，请设置ALIYUN_ACCESS_KEY_ID/ALIYUN_ACCESS_KEY_SECRET")
                return False
            config = open_api_models.Config(
                access_key_id=ak_id,
                access_key_secret=ak_secret,
                endpoint='dysmsapi.aliyuncs.com'
            )
            client = Dysmsapi20170525Client(config)

            params = {
                'phone_numbers': phone,
                'sign_name': settings.aliyun_sms_sign_name,
                'template_code': settings.aliyun_sms_template_code,
                'template_param': json.dumps({'code': code}, ensure_ascii=False)
            }
            request = dysms_models.SendSmsRequest(**params)
            runtime = util_models.RuntimeOptions()
            resp = client.send_sms_with_options(request, runtime)
            try:
                ok = getattr(resp.body, 'code', '') == 'OK'
            except Exception:
                ok = False
            if not ok:
                try:
                    print(f"阿里云短信返回错误: code={getattr(resp.body, 'code', '')}, message={getattr(resp.body, 'message', '')}")
                except Exception:
                    pass
            return bool(ok)
        except Exception as e:
            print(f"阿里云短信发送异常: {str(e)}")
            return False
    
    async def _send_tencent_sms(self, phone: str, code: str, scene: str) -> bool:
        """
        发送腾讯云短信
        
        Args:
            phone: 手机号
            code: 验证码
            scene: 使用场景
        
        Returns:
            bool: 发送是否成功
        """
        # TODO: 实现腾讯云短信发送
        # 需要配置腾讯云SecretId、SecretKey、应用ID、签名、模板等
        print(f"[腾讯云短信] 发送到 {phone}: 验证码 {code}")
        return True

# 创建全局SMS服务实例
sms_service = SMSService()

async def send_sms_code(phone: str, scene: str = "login") -> Dict[str, Any]:
    """
    发送短信验证码（全局函数）
    
    Args:
        phone: 手机号
        scene: 使用场景
    
    Returns:
        Dict[str, Any]: 发送结果
    """
    return await sms_service.send_verification_code(phone, scene)

async def verify_sms_code(phone: str, code: str, scene: str = "login") -> bool:
    """
    验证短信验证码（全局函数）
    
    Args:
        phone: 手机号
        code: 验证码
        scene: 使用场景
    
    Returns:
        bool: 验证是否成功
    """
    return await sms_service.verify_code(phone, code, scene)